/*
#include "gndsession.h"
#include "gndconnect.h"
#include "gndmodule.h"
*/

#include "gnd.h"

typedef struct {
    GndSession * session;
    int len;
    gchar * data;
    GndSessionStatus status;
    gpointer other_data;
} GndSessionIdleData;

gboolean gnd_session_connect_finished_idle_cb(GndSessionIdleData * data);
gboolean gnd_session_accept_data_socket_cb(GSocket * socket,
                                           GIOCondition io_condition,
                                           gpointer data);

GndSession * gnd_session_new(GndModule * module)
{
    GndSession * session = g_new(GndSession, 1);
    session->module = module;
    session->mutex_socket = g_new(GRecMutex, 1);
    g_rec_mutex_init(session->mutex_socket);
    return session;
}

gboolean gnd_session_free(GndSession * session)
{
    if (session) {
        if (session->mutex_socket) {
            g_free(session->mutex_socket);
        }
        g_free(session);
        return TRUE;
    }
    return FALSE;
}

//run in network thread
//Session的socket进行异步连接的回调
//Session建立连接时在连接线程回调，在此回调主线程的模块处理函数
//往connect里添加session时socket的连接状态会通过该回调函数返回
gboolean gnd_session_connect_finished_cb(GSocket * socket,
                                         GIOCondition condition,
                                         GndSession * self)
{
    GndSessionStatus status;
    if ((condition & G_IO_OUT) != G_IO_OUT ||
            !g_socket_check_connect_result(socket, NULL) ||
            !g_socket_is_connected(socket)) {
        status = GND_SESSION_STATUS_FAILURE;
        //return FALSE;
    } else {
        status = GND_SESSION_STATUS_SUCCEED;
    }

    //创建一个在UI线程运行的回调
    GSource * idle_source = g_idle_source_new();
    GndSessionIdleData * data = g_new(GndSessionIdleData, 1);
    data->session = self;
    data->status = status;
    g_source_set_callback(idle_source,
                          (GSourceFunc) gnd_session_connect_finished_idle_cb,
                          data, NULL);
    g_source_attach(idle_source, gnd_connect_get_main_context(self->connect));
    return FALSE;
}

//run in network thread
//弃用
gboolean gnd_session_data_arrive_cb(GSocket * socket,
                                    GIOCondition condition,
                                    GndSession * self)
{}

//通过该session发送数据
//必须运行在主线程
gboolean gnd_session_send_data(GndSession * self, gchar * data, int len)
{
    g_assert(self);

    g_rec_mutex_lock(self->mutex_socket);
    if (!self->socket) {
        g_rec_mutex_unlock(self->mutex_socket);
        return FALSE;
    }

    //等待缓冲区可写
    while (!g_socket_condition_wait(self->socket, G_IO_OUT, NULL, NULL)) {
        g_warning("waiting for writable");
    }
    gssize write_size = g_socket_send(self->socket, data, len, NULL, NULL);

    if (write_size == -1 || write_size != len) {
        g_warning("write data uncomplete");
        return TRUE;
    }
    g_rec_mutex_unlock(self->mutex_socket);

    return TRUE;
}

//必须运行在主线程
//接受该session的数据
//timeout : s
//callback实际上是一个data是GndSessionData的Idle source callback
gboolean gnd_session_accept_data(GndSession *self, guint timeout, GSourceFunc callback)
{

    g_assert(self);

    g_rec_mutex_lock(self->mutex_socket);
    if (!self->socket) {
        g_rec_mutex_unlock(self->mutex_socket);
        return FALSE;
    }

    g_socket_set_timeout(self->socket, timeout);
    GSource * socket_source = g_socket_create_source(self->socket, G_IO_IN, NULL);

    GndSessionData * data = g_new(GndSessionData, 1);
    data->session = self;
    data->other_func = (GFunc) callback;

    //创建一个运行在网络线程的source
    g_source_set_callback(socket_source,
                          (GSourceFunc)gnd_session_accept_data_socket_cb,
                          (gpointer) data,
                          NULL);

    if(!gnd_connect_attach_source(self->connect, socket_source))
    {
        g_source_unref(socket_source);
        g_free(data);
        return FALSE;
    }

    g_rec_mutex_unlock(self->mutex_socket);

    return TRUE;
}

//必须运行在主线程
//关闭该session
gboolean gnd_session_close(GndSession * self)
{
    g_rec_mutex_lock(self->mutex_socket);
    if (!self->socket) {
        g_socket_close(self->socket, NULL);
        g_object_unref(self->socket);
        self->socket = NULL;
    }
    g_rec_mutex_unlock(self->mutex_socket);
}

gboolean gnd_session_isclosed(GndSession *self)
{
    g_assert(self);

    g_rec_mutex_lock(self->mutex_socket);
    if (!self->socket) {
        g_rec_mutex_unlock(self->mutex_socket);
        return TRUE;
    } else {
        g_rec_mutex_unlock(self->mutex_socket);
        return FALSE;
    }
}

//Session的socket连接后在UI线程运行的回调，在此直接调用该session绑定的module的方法
gboolean gnd_session_connect_finished_idle_cb(GndSessionIdleData *data)
{
    g_assert(data);

    gnd_module_connect_status(data->session->module, data->status,
                              data->other_data);
    g_free(data);
    return FALSE;
}

//run in network thread
//session的socket接受到数据时运行在网络线程的回调
//在此使模块的回调函数在UI线程运行
gboolean gnd_session_accept_data_socket_cb(GSocket *socket, GIOCondition io_condition, gpointer data)
{
    GndSessionData * session_data = (GndSessionData *) data;
    g_assert(data);
    GndSessionData * ret_data = g_new(GndSessionData, 1);
    ret_data->session = session_data->session;

    if ((io_condition & G_IO_IN) != G_IO_IN ||
            (io_condition & G_IO_ERR) == G_IO_ERR) {
        ret_data->status = GND_SESSION_STATUS_FAILURE;
    } else {
        ret_data->data = g_new(char, BUFFER_SIZE);
        ret_data->len = g_socket_receive(socket,
                                         ret_data->data, BUFFER_SIZE,
                                         NULL, NULL);
        int time = 1;       //缓冲区扩充倍数
        while (ret_data->len == BUFFER_SIZE * time) {                           //缓冲区不足
            time *= 10;
            ret_data->data = g_realloc(ret_data->data, BUFFER_SIZE * time);     //缓冲区重新分配
            ret_data->len = ret_data->len + g_socket_receive(socket,
                                                             ret_data->data + ret_data->len,
                                                             BUFFER_SIZE * time - BUFFER_SIZE * time / 10,
                                                             NULL, NULL);       //将未读完的数据继续读
        }

        if (ret_data->len == 0 || ret_data->len == -1) {
            ret_data->status = GND_SESSION_STATUS_FAILURE;
            g_free(ret_data->data);
            ret_data->data = NULL;
        } else {
            ret_data->status = GND_SESSION_STATUS_SUCCEED;
        }

    }

    GndConnect * connect = session_data->session->connect;
    //创建一个在UI线程运行回调函数的source
    GSource * idle_source = g_idle_source_new();
    g_source_set_callback(idle_source,
                          (GSourceFunc) session_data->other_func,
                          (gpointer) ret_data,
                          NULL);
    g_source_attach(idle_source, gnd_connect_get_main_context(connect));

    return FALSE;
}
